using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

using Athena.Objects;
using Athena.Tools;

using WeifenLuo.WinFormsUI.Docking;
using System.Collections;


using System.ComponentModel.Design;
using System.Windows.Forms.Design;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;


namespace Athena
{

    [PropertyTabAttribute(typeof(TypeCategoryTab), PropertyTabScope.Component)]
    public partial class PropertyControl : IGUITool
    {

        protected static List<IGUITool> IGUITools = new List<IGUITool>();
        private IGUITool _ActiveTool = null;
        public IGUITool ActiveTool
        {
            get { return _ActiveTool; }
            set { _ActiveTool = value; }
        }

        /// <summary>
        /// Adds a new property editor that will pop-up instead of the default property editor
        /// </summary>
        /// <param name="oPropertyEditor"></param>
        /// <returns></returns>
        public static bool AddPropertyEditor(IGUITool oPropertyEditor)
        {
            if (oPropertyEditor == null || IGUITools.Contains(oPropertyEditor)) return false;
            IGUITools.Add(oPropertyEditor);
            return true;
        }

        public PropertyControl()
        {
            _bHasContextMenuItems = true;
            _bRequiresNewToolWindow = true;

            InitializeComponent();
            //cmbItems.DataSource = Program.Model.Drawables;
            Text = "Property Editor";
            ToolManager.Instance.RegisterTool("Property Editor", "Provides a convenient way of changing properties of selected Objects", this);
            Model = CurrentModel;

            ComboBoxItems = new string[] { "CurrentModel", "Canvas", "Grid Tool" };

            if (!ToolManager.Instance.PropertyEditors.Contains(this))
                ToolManager.Instance.PropertyEditors.Insert(0,this);
        }

        /// <summary>
        /// The items of the combobox ... currently only recognizes "CurrentModel" and tool names.
        /// </summary>
        public string[] ComboBoxItems
        {
            get
            {
                List<string> oItems = new List<string>();
                foreach (object o in cmbItems.Items)
                {
                    oItems.Add((string)o);
                }
                return oItems.ToArray();
            }
            set
            {
                cmbItems.Items.Clear();
                cmbItems.AutoCompleteCustomSource.Clear();

                if (value != null)
                {
                    cmbItems.Items.AddRange(value);
                    cmbItems.AutoCompleteCustomSource.AddRange(value);
                }

            }
        }

        public override void SetupToolStrip(ToolStrip oStrip)
        {
            ToolStripButton oButton = new ToolStripButton();
            oButton.Text = "&Properties ...";
            oButton.ToolTipText = "Provides a convenient way of changing properties of selected Objects";
            oButton.Click += new EventHandler(ShowHide_Click);
            oStrip.Items.Add(oButton);
        }

        public override void SetupDockPanel(WeifenLuo.WinFormsUI.Docking.DockPanel oPanel)
        {
            this.DockPanel = oPanel;
        }

        public override bool Handles(List<Type> oTypes)
        {
            if (oTypes.Count < 1) return false;
            /*foreach (IGUITool ig in IGUITools)
            {
                if (ig.Handles(oTypes)) return false;
            }*/
            return true;      
        }

        public override void SetupContextMenu(ToolStripMenuItem oStrip)
        {
            oStrip.Text = "Properties ...";
            oStrip.ToolTipText = "Provides a convenient way of changing properties of selected Objects";
            oStrip.Click += new EventHandler(oItem_Click);
        }

        public override void ShowHide_Click(object sender, EventArgs e)
        {
            foreach (IGUITool ig in IGUITools)
            {
                if (ig.Handles(Model.SelectedTypes))
                {
                    ig.ShowHide_Click(sender, e);
                }
            }
            if (IsHidden)
            {
                Show();
                propertyGrid1.SelectedObjects = Model.Selected.ToArray();
            }
            else
            {
                Hide();
            }
        }

        /// <summary>
        /// Hide all the property editors that do not apply to the currently selected list of items
        /// </summary>
        void HideInactiveEditors()
        {
            foreach (IGUITool ig in IGUITools)
            {
                if (!ig.Handles(Model.SelectedTypes))
                {
                    ig.Hide();
                }
            }
        }

        /// <summary>
        /// Show only the property editors that apply to the currently selected list of items
        /// </summary>
        void ShowActiveEditors()
        {
            foreach (IGUITool ig in IGUITools)
            {
                if (ig.Handles(Model.SelectedTypes) && ig.IsHidden)
                {
                    if (DockPanel != null && VisibleState != DockState.Unknown)
                    {
                        ig.Show(DockPanel, VisibleState);                        
                    }
                    else
                    {
                        ig.ShowHint = VisibleState;
                        ig.Show();                        
                    }
                    break;
                }
            }
        }

        /// <summary>
        /// Counts the number of active non-default property editors
        /// </summary>
        /// <returns></returns>
        public static int CountActiveEditors()
        {
            int count = 0;
            foreach (IGUITool ig in IGUITools)
            {
                if (!ig.IsHidden) ++count;
            }

            return count;
        }

        public override void oItem_Click(object sender, EventArgs e)
        {
            Show();
            if (!IsHidden)
            {
                propertyGrid1.SelectedObjects = Model.Selected.ToArray();
            }
        }

        /*public override void OnKeyDown(KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.Return:
                    HideInactiveEditors();
                    ShowActiveEditors();
                    if (!IsHidden)
                    {
                        propertyGrid1.SelectedObjects = Model.Selected.ToArray();
                    }                    
                    break;

                default:
                    break;
            }
        }*/

        
        public override void OnItemDoubleClick(HandledMouseEventArgs e)
        {
            //HideInactiveEditors();
            //ShowActiveEditors();

            //if (IsHidden)
            //    Show();
            
            bool allSpecies = CurrentModel.Selected.Count > 0;

            foreach (IDrawable id in CurrentModel.Selected)
            {
                if (id.Item != null && !(id.Item is Core.Species))
                {
                    allSpecies = false;
                    break;
                }
            }

            if (!allSpecies)
            {
                Hide();
            }
            else
                Show();

            if (!IsHidden)
            {
                propertyGrid1.SelectedObjects = Model.Selected.ToArray();
            }
        }

        private Model _oModel = null;

        public Model Model
        {
            get { return _oModel; }
            set
            {
                if (_oModel != null)
                {
                    _oModel.SelectionChanged -= _oModel_SelectionChanged;
                }
                _oModel = value;
                _oModel.SelectionChanged += new Model.SelectionChangedDelegate(_oModel_SelectionChanged);

            }
        }
        void PropertyControl_FormClosing(object sender, System.Windows.Forms.FormClosingEventArgs e)
        {
            this.Hide();
            foreach (IGUITool ig in IGUITools)
            {
                ig.Hide();
            }
            e.Cancel = true;
        }

        void _oModel_SelectionChanged(object sender, Model.SelectionEventArgs e)
        {
            if (e.SelectedObjects == null || e.SelectedObjects.Count == 0)
            {
                propertyGrid1.SelectedObject = null;
                Hide();
            }
            /*
            if (CountActiveEditors() > 0 || !IsHidden)
            {
                if ((Model.SelectedTypes.Count > 0) && (ActiveTool == null || !ActiveTool.Visible || !ActiveTool.Handles(Model.SelectedTypes)))
                {
                    HideInactiveEditors();
                    ShowActiveEditors();
                }
                //HideInactiveEditors();
                //ShowActiveEditors();
            }*/
            /*
            int hasSpecies = 0;
            int hasReaction = 0;
            int hasPart = 0;
            int hasContainer = 0;

            foreach (IDrawable id in CurrentModel.Selected)
            {
                if (id.Item != null)
                {
                    if (id.Item is Core.Species)
                        hasSpecies = 1;
                    if (id.Item is Core.Part)
                        hasPart = 1;
                    if (id.Item is Core.Edge)
                        hasReaction = 1;
                    if (id.Item is Core.Container)
                        hasContainer = 1;
                }
            }

            if ((hasSpecies + hasPart + hasReaction + hasContainer) > 1)
            {
                Show();
            }
            else
            {
                Hide();
            }         */       

            if (!IsHidden)
            {
                propertyGrid1.SelectedObjects = Model.Selected.ToArray();
            }
        }

        /// <summary>
        /// Sets the currently selected object for the Property Editor
        /// </summary>
        public Object SelectedObject
        {
            get { return this.propertyGrid1.SelectedObject; }
            set
            {
                this.propertyGrid1.SelectedObject = value;
            }
        }

        /// <summary>
        /// Sets the currently selected objects for the Property Editor
        /// </summary>
        public List<Object> SelectedObjects
        {
            get
            {
                List<Object> oList = new List<object>();
                oList.AddRange(this.propertyGrid1.SelectedObjects);
                return oList;
            }
            set
            {
                this.propertyGrid1.SelectedObjects = value.ToArray();
            }
        }

        private void propertyGrid1_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
        {
            //propertyGrid1.SelectedObjects = propertyGrid1.SelectedObjects;
            HistoryManager.History.SetControllPoint("property changed");
            CurrentModel.OnModelChanged(EventArgs.Empty);
            Utility.Refresh();
        }

        private void cmdOK_Click(object sender, EventArgs e)
        {

        }

        private void cmbItems_SelectedValueChanged(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty((string)cmbItems.Text))
            {
                string sCommand = (string)cmbItems.Text;
                if (cmbItems.Items.Contains(sCommand)) { SelectItem(sCommand); return; }
                else
                {
                    try
                    {
                        ITool oTemp = ToolManager.Instance.GetTool(sCommand);
                        cmbItems.Items.Add(sCommand);
                        cmbItems.AutoCompleteCustomSource.Add(sCommand);
                        SelectedObject = oTemp;
                    }
                    catch (Exception)
                    {
                    }
                }
            }
            SelectItem((string)cmbItems.SelectedItem);
        }

        private void SelectItem(string sItem)
        {
            switch (sItem)
            {
                case "CurrentModel":
                    SelectedObject = CurrentModel;
                    break;
                case "Canvas":
                    SelectedObject = Utility.Panel;
                    break;
                default:
                    try
                    {
                        SelectedObject = ToolManager.Instance.GetTool((string)cmbItems.SelectedItem);
                    }
                    catch (Exception)
                    {
                    }
                    break;
            }
        }
    }
    // A TypeCategoryTab property tab lists properties by the 
    // category of the type of each property.
    [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
    public class TypeCategoryTab : PropertyTab
    {
        [BrowsableAttribute(true)]
        // This string contains a Base-64 encoded and serialized example property tab image.
        private string img = "AAEAAAD/////AQAAAAAAAAAMAgAAAFRTeXN0ZW0uRHJhd2luZywgVmVyc2lvbj0xLjAuMzMwMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWIwM2Y1ZjdmMTFkNTBhM2EFAQAAABVTeXN0ZW0uRHJhd2luZy5CaXRtYXABAAAABERhdGEHAgIAAAAJAwAAAA8DAAAA9gAAAAJCTfYAAAAAAAAANgAAACgAAAAIAAAACAAAAAEAGAAAAAAAAAAAAMQOAADEDgAAAAAAAAAAAAD///////////////////////////////////9ZgABZgADzPz/zPz/zPz9AgP//////////gAD/gAD/AAD/AAD/AACKyub///////+AAACAAAAAAP8AAP8AAP9AgP////////9ZgABZgABz13hz13hz13hAgP//////////gAD/gACA/wCA/wCA/wAA//////////+AAACAAAAAAP8AAP8AAP9AgP////////////////////////////////////8L";

        public TypeCategoryTab()
        {
        }

        // Returns the properties of the specified component extended with 
        // a CategoryAttribute reflecting the name of the type of the property.
        public override System.ComponentModel.PropertyDescriptorCollection GetProperties(object component, System.Attribute[] attributes)
        {
            PropertyDescriptorCollection props;
            if (attributes == null)
                props = TypeDescriptor.GetProperties(component);
            else
                props = TypeDescriptor.GetProperties(component, attributes);

            List<PropertyDescriptor> propArray = new List<PropertyDescriptor>();
            for (int i = 0; i < props.Count; i++)
            {

                // Create a new PropertyDescriptor from the old one, with 
                // a CategoryAttribute matching the name of the type.
                if (props[i].ComponentType == typeof(PropertyControl))
                    propArray.Add(TypeDescriptor.CreateProperty(props[i].ComponentType, props[i], new CategoryAttribute(props[i].Category)));
            }
            return new PropertyDescriptorCollection(propArray.ToArray());
        }

        public override System.ComponentModel.PropertyDescriptorCollection GetProperties(object component)
        {
            return this.GetProperties(component, null);
        }

        // Provides the name for the property tab.
        public override string TabName
        {
            get
            {
                return "Properties by Type";
            }
        }

        // Provides an image for the property tab.
        public override System.Drawing.Bitmap Bitmap
        {
            get
            {
                Bitmap bmp = new Bitmap(DeserializeFromBase64Text(img));
                return bmp;
            }
        }

        // This method can be used to retrieve an Image from a block of Base64-encoded text.
        private Image DeserializeFromBase64Text(string text)
        {
            Image img = null;
            byte[] memBytes = Convert.FromBase64String(text);
            IFormatter formatter = new BinaryFormatter();
            MemoryStream stream = new MemoryStream(memBytes);
            img = (Image)formatter.Deserialize(stream);
            stream.Close();
            return img;
        }
    }

}
